/* 
 * Copyright (c) 2016 Qualcomm Technologies, Inc. 
 * All Rights Reserved. 
 * Qualcomm Technologies, Inc. Confidential and Proprietary. 
 */
package com.thunder.rebot.rebotclient.vam.utils;

import android.content.ContentResolver;
import android.content.Context;
import android.content.res.Configuration;
import android.content.res.Resources;
import android.database.Cursor;
import android.graphics.Bitmap;
import android.graphics.Bitmap.Config;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.PointF;
import android.graphics.PorterDuff;
import android.graphics.PorterDuffXfermode;
import android.graphics.RectF;
import android.net.Uri;
import android.os.Environment;
import android.provider.MediaStore;
import android.util.Log;
import android.util.TypedValue;

import com.thunder.rebot.rebotclient.vam.entity.FaceFrame;
import com.thunder.rebot.rebotclient.vam.entity.MapperParams;
import com.thunder.rebot.rebotclient.vam.entity.ViewSize;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.ArrayList;

/**
 * The class for utility functions
 */
public class Utils {
    private static String TAG = Utils.class.getSimpleName();

    private static final String SETTINGS_DIR = "ipcapp";

    public static final int MAX_ABSENCE = 8;

    public static  boolean CAMPTURE_ON_LIVE = true;

    public static final long RENDERER_QUEUE_TIMEOUT = 10000; // micro sec
    public static final int RENDERER_POLL_TIMER = 500;// ms
    public static final int INTERRUPT_THREAD_AFTER = 200; // ms

    public static final String CODEC_TYPE_AVC = "video/avc";

    public static final int CANNED_MOVIE_WD = 1920;
    public static final int CANNED_MOVIE_HT = 1080;
    public static final byte[] CANNED_HEADER_SPS = { 0, 0, 0, 1, 103, 66, -64, 13, -102, 116, 3,
            -64, 17, 63, 46, 2, 32, 0, 0, 3, 0, 32, 0, 0, 6, 81, -30, -123, 84 };
    public static final byte[] CANNED_HEADER_PPS = { 0, 0, 0, 1, 104, -50, 60, -128 };

    public static final int MAX_PICTURE_ID = 99999;
    private static String[] queryColumns = { MediaStore.Images.Media.DATA,
            MediaStore.Images.Media._ID };
    
    // vam config types
    public static final int CMD_VAM_CFG_FD = 1;
    public static final int CMD_VAM_CFG_FR = 2;
    public static final int CMD_VAM_CFG_MD = 3;
    public static final int CMD_VAM_CFG_IT = 4;

    // service-activity messages

    /**
     * Enum for service status
     */
    public enum ServiceStatus {
        ERROR(-1), NONE(0), BUSY(1), STREAM_AND_EVENTS(2), EVENTS_ONLY(3);

        private final int id;

        private ServiceStatus(int id) {
            this.id = id;
        }

        /**
         * The id of the service status
         * 
         * @return
         */
        public int getId() {
            return id;
        }

        /**
         * Get the device application status type from the value
         * 
         * @param id
         *            status id
         * @return status type
         */
        public static ServiceStatus getFromId(int id) {
            for (ServiceStatus status : values()) {
                if (status.id == id) {
                    return status;
                }
            }
            return NONE;
        }
    }

    /**
     * Get the float value from dimension float variable
     * 
     * @param resources
     *            application resource reference
     * @param resourceId
     *            resource id
     * @return float value
     */
    public static float getFloatFromDimen(Resources resources, int resourceId) {
        TypedValue typedValue = new TypedValue();
        resources.getValue(resourceId, typedValue, true);
        return typedValue.getFloat();
    }

    /**
     * Save the bitmap to gallery
     * 
     * @param context
     *            application context
     * @param jsonData
     *            json data to be written
     * @param fileName
     *            file to be written
     * @return true if written successfully, false otherwise
     */
    public static boolean saveJsonFile(Context context, String jsonData, String fileName) {
        String publicStoragePath = getJsonFileDir();
        String fullPath = publicStoragePath + "/" + fileName + ".json";

        Log.d(TAG, "dir: " + publicStoragePath + ", full: " + fullPath);

        File dir = new File(publicStoragePath);
        if (dir.isDirectory() == false) {
            if (dir.mkdirs() == false) {
                Log.d(TAG, "Unable to make directory for saving images");
                return false;
            }
        }

        File jsonFile = new File(fullPath);
        if (jsonFile.exists()) {
            jsonFile.delete();
        }
        try {
            FileOutputStream outputStream = new FileOutputStream(jsonFile);
            outputStream.write(jsonData.getBytes());
            outputStream.close();
            return true;

        } catch (IOException exp) {
            exp.printStackTrace();
        }

        return false;
    }

    /**
     * get the setting files directory
     * 
     * @return setting files directory
     */
    public static String getJsonFileDir() {
        return Environment.getExternalStorageDirectory() + "/" + Utils.SETTINGS_DIR;
    }

    /**
     * 
     * gets the list of the setting files
     * 
     * @return setting files list
     */
    public static ArrayList<String> getSettingFiles() {
        String publicStoragePath = getJsonFileDir();
        File dir = new File(publicStoragePath);
        File files[] = dir.listFiles();
        if (files == null || files.length == 0) {
            return null;
        }

        ArrayList<String> jsonFiles = new ArrayList<String>();
        for (int i = 0; i < files.length; i++) {
            jsonFiles.add(files[i].getName());
        }
        return jsonFiles;
    }

    /**
     * Creates a round icon
     * 
     * @param image
     *            image to be drawn in rounded image
     * @param iconSize
     *            icon size
     * @param shadowWidth
     *            shadow width
     * @return rounded image
     */
    public static Bitmap createRoundIcon(Bitmap image, float iconSize, float shadowWidth) {
        float imageCenter = iconSize / 2;

        // create mask
        Paint maskPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        maskPaint.setColor(Color.BLACK);
        Bitmap maskImage = Bitmap.createBitmap((int) iconSize, (int) iconSize, Config.ARGB_8888);
        Canvas maskCanvas = new Canvas(maskImage);
        maskCanvas.drawCircle(imageCenter, imageCenter, imageCenter - shadowWidth, maskPaint);

        // create round icon image
        Bitmap roundImage = Bitmap.createBitmap((int) iconSize, (int) iconSize, Config.ARGB_8888);
        Canvas canvas = new Canvas(roundImage);

        final RectF destinationRect = new RectF(0, 0, iconSize, iconSize);
        final Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);

        canvas.drawBitmap(image, null, destinationRect, paint);
        paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_IN));
        canvas.drawBitmap(maskImage, 0, 0, paint);
        paint.setXfermode(null);

        Bitmap output = null;
        if (shadowWidth > 0) {// draw final image (shadow and round icon)
            output = Bitmap.createBitmap((int) iconSize, (int) iconSize, Config.ARGB_8888);
            Canvas shadowCanvas = new Canvas(output);
            Paint shadowPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
            shadowPaint.setColor(Color.WHITE);
            shadowPaint.setShadowLayer(shadowWidth, 0.0f, 0.0f, Color.BLACK);
            shadowCanvas.drawCircle(imageCenter, imageCenter, imageCenter - shadowWidth,
                    shadowPaint);
            shadowCanvas.drawBitmap(roundImage, 0, 0, paint);

            roundImage.recycle();
        } else {
            output = roundImage;
        }
        maskImage.recycle();
        return output;
    }

    private static String getImagePathById(ContentResolver contentResolver, String id) {
        String filePath = "";
        String[] pathColumn = { MediaStore.Images.Media.DATA };

        String selectionArgs = MediaStore.Images.Media._ID + "=?";

        Cursor cursor = contentResolver.query(MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
                pathColumn, selectionArgs, new String[] { id }, null);

        int columnIndex = cursor.getColumnIndex(pathColumn[0]);

        if (cursor.moveToFirst()) {
            filePath = cursor.getString(columnIndex);
        }
        cursor.close();
        return filePath;
    }

    /**
     * adds the images paths to the array list
     * 
     * @param contentResolver
     *            activity content resolver
     * @param imagePaths
     *            array list to be filled
     * @param pathURI
     *            image path URI
     */
    public static boolean addPath(ContentResolver contentResolver, ArrayList<String> imagePaths,
                                  Uri pathURI) {
        Cursor cursor = contentResolver.query(pathURI, queryColumns, null, null, null);
        cursor.moveToFirst();

        int columnIndex = cursor.getColumnIndexOrThrow(MediaStore.Images.Media.DATA);
        String path = cursor.getString(columnIndex);
        cursor.close();
        if (path == null) {
            // HACK
            String key = "image%3A";
            int index = pathURI.toString().indexOf(key);
            if (index != -1) {
                String id = pathURI.toString().substring(index + key.length());
                path = getImagePathById(contentResolver, id);
                Log.d(TAG, "img fetched from id: " + id + "=>" + path);
            } else {
                Log.d(TAG, "Not able to fetch image: " + pathURI.toString());
            }

        } else {
            Log.d(TAG, "img fetched from ui: " + path);
        }

        if (path == null) {
            return false;
        }

        imagePaths.add(path);
        return true;
    }

    /**
     * Convert the image rectangles to screen rectangles
     * 
     * @param faceFrames
     *            face frames
     * @param params
     *            mapper parameters
     * @return mapped face frames
     */
    public static ArrayList<FaceFrame> image2ScreenFaceFrames(ArrayList<FaceFrame> faceFrames, MapperParams params) {
        ArrayList<FaceFrame> screenFaceframes= new ArrayList<FaceFrame>();
        for (FaceFrame faceFrame : faceFrames) {
            RectF rect = faceFrame.getRect();
            float left = rect.left * params.scale + params.unused.width / 2;
            float top = rect.top * params.scale + params.unused.height / 2;
            float right = rect.right * params.scale + params.unused.width / 2;
            float bottom = rect.bottom * params.scale + params.unused.height / 2;
            Log.d("zgd", "left:"+left+"");
            Log.d("zgd", "top:"+ top+"");
            Log.d("zgd", "right:"+ right+"");
            Log.d("zgd",  "bottom:"+bottom+"");
            RectF screenRect = new RectF(left, top, right, bottom);
            screenFaceframes.add(new FaceFrame(faceFrame.getId(), screenRect));
        }
        Log.d("image2ScreenFaceFrames", "image2ScreenFaceFrames");
        return screenFaceframes;
    }

    /**
     * gets the mapper parameters
     * 
     * @param actual
     *            actual view size
     * @param angle
     *            angle of the screen
     * @param screen
     *            screen size
     * @return mapper params
     */
    public static MapperParams getMapperParams(ViewSize actual, int angle, ViewSize screen) {
        if (angle == 90 || angle == 270) {
            actual = new ViewSize(actual.height, actual.width);
        }
        float horizontalRatio = (float) actual.width / screen.width;
        float verticalRatio = (float) actual.height / screen.height;
        float actualScaled = Math.max(horizontalRatio, verticalRatio);

        float unusedWd = screen.width - (float) actual.width / actualScaled;
        float unusedHt = screen.height - (float) actual.height / actualScaled;
        ViewSize unused = new ViewSize((int) unusedWd, (int) unusedHt);

        float displayRatio = (float) (screen.width - unused.width) / actual.width;
        return new MapperParams(screen, unused, displayRatio);
    }

    /**
     * gets the mapper parameters
     *
     * @param actual actual size
     * @param parent parent mapper params
     * @return mapper params
     */
    public static MapperParams getMapperParams(ViewSize actual, MapperParams parent) {
        float parentTakenWd = parent.screen.width - parent.unused.width;
        float parentTakenHt = parent.screen.height - parent.unused.height;

        float horizontalRatio = (float) actual.width / parentTakenWd;
        float verticalRatio = (float) actual.height / parentTakenHt;
        float actualScaled = Math.max(horizontalRatio, verticalRatio);

        float unusedWd = parentTakenWd - (float) actual.width / actualScaled;
        float unusedHt = parentTakenHt - (float) actual.height / actualScaled;
        ViewSize unused = new ViewSize((int) unusedWd + parent.unused.width, (int) unusedHt + parent.unused.height);

        float displayRatio = (parentTakenWd - unusedWd) / actual.width;
        return new MapperParams(parent.screen, unused, displayRatio);
    }

    /**
     * Checks if the app is portrait mode or not
     * 
     * @param context
     *            activity context
     * @return truie if portrait mode, false otherwise
     */
    public static boolean isPortraitMode(Context context) {
        return (context.getResources().getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT);
    }

    /**
     * Appends csv object
     *
     * @param prevObjs previous csv
     * @param newObjs  new csv to be added
     * @return appended csv
     */
    public static String appendCsvObj(String prevObjs, String newObjs) {
        return (prevObjs == null || prevObjs.isEmpty()) ? newObjs : prevObjs + "," + newObjs;
    }

    /**
     * Method to get the selected rect after translating according to params
     *
     * @param params mapper params for the snap
     * @return translated rect
     */
    public static final int MAX_SIZE_BNDRY = 10000;
    public static PointF[] screen2VAMPoints(PointF[] pts, MapperParams params) {
        float finalWidth = (params.screen.width - params.unused.width)/params.scale;
        float finalHeight= (params.screen.height - params.unused.height)/params.scale;
        Log.d(TAG, "final size: " + finalWidth + " X " + finalHeight);
        PointF[] retPoints = new PointF[pts.length];
        for (int i = 0; i < pts.length; i++) {
            float newX = (pts[i].x - params.unused.width / 2) / params.scale;
            float newY = (pts[i].y - params.unused.height / 2) / params.scale;

            Log.d(TAG, "Translated 1 [" + pts[i].x + "," + pts[i].y + "]=>[" + newX + "," + newY + "]");
            // translate from 1- 10000
            newX = newX/finalWidth * MAX_SIZE_BNDRY;
            newY = newY/finalHeight * MAX_SIZE_BNDRY;

            retPoints[i] = new PointF(newX, newY);
            Log.d(TAG, "Translated 2 [" + pts[i].x + "," + pts[i].y + "]=>[" + newX + "," + newY + "]");
        }
        return retPoints;
    }
}
